/**
  *	Networking semester project
  */

#include <avr/io.h>			// Contains port name definitions, etc.
#include <avr/interrupt.h>	// Contains declaration for sei()
#include "globaldef.h"		// Contains definition for F_CPU (needed by delay.h)
#include <util/delay.h>		// Contains _delay_ms and _delay_us functions
#include <inttypes.h>		// Contains C99 data types
#include "lcd.h"			// Contains many lcd functions
#include <stdlib.h>
#include <stdbool.h>

#define ADDRESS 0x13

//State Flags
volatile bool BUSY = false;
volatile bool IDLE = false;
volatile bool COLLISION = false;
volatile bool SENDING = false;
volatile bool DETECTEDCOLLISION = false;
void initialize();
char USARTReadChar();
void USARTInit(uint16_t ubrr_value);
void USARTWriteChar(char);
void write1();
void write0();
void send_message(char input[]);
void write_message_buffer();
void appendHeader(char dest[]);
void writeChar(uint8_t out);

//track the value of the previous bit part
//read in (1 = high, 0 = low)
volatile int LASTBITPART = 1;

//tracks whether the next bit part read in
//will be the second half of a bit
volatile bool BITPARTTWO = false;

volatile char currentChar = 0x00;
volatile uint8_t bitCount = 0;
volatile unsigned char message_buffer[1956];
volatile uint16_t our_index = 0;

//dummy int used to keep loops from being
//taken out by optomizer
int doSomething = 0;

//buffer to hold message before it is sent out
volatile unsigned char out_buffer[1956];
volatile uint16_t out_buf_index = 0;
volatile uint16_t currentOutPosition = 0;
volatile bool firstPartSent = false;
volatile bool messageBuffered = false;


int main()
{
	
	initialize();
		
	//Infinate loop to see what the status is
	while (1)

	{

		//start new message
		lcd_clear();
		lcd_home();
		
		int i = 0;
		char input[244];
		
		//while the message is less then 128 and message not sent
		//wait for input
		while(i < 244){

		 	char temp = USARTReadChar();

			
   			lcd_print_char(temp);

			//if enter pressed then
			//break out of loop and send message
			if(temp == 13){
				input[i] = temp;
				break;
			//else add character to message
			}else{
				input[i] = temp;
				//echo it to hyper terminal
				USARTWriteChar(temp);
			}
		
			//increment counter;
			i++;
		}
		SENDING = true;
		//Disable external interupt 1 while sending. We know the channel will be busy
		GICR = (0<<INT1);
		//wait at least 2.5ms so we know that timer 2 is overflowing every 2.5ms
		//_delay_ms(2.5);		

		while(!IDLE){
			doSomething++;
		}
		
		
		send_message(input);
		while(SENDING){
			PORTB = 0b01111111;
			//lcd_clear();
		}
		//Enable external interupt 1
		GICR = (1<<INT1);
		//Setup INT1 to trigger on a logical change
		MCUCR = (0<<ISC11)|(1<<ISC10);
		
		
		

	}	

	
}

/*************STARTUP FUNCTIONS*********************/

void initialize(){
	//Setup pin 7 for output and pin 6 as input
	DDRD = 0b10000000;
	PORTD= 0xFF;
	PIND = 0xFF;

	//Setup LEDs
	DDRB =0xFF;
	PORTB = 0x00;

	lcd_init('C');
	lcd_clear();
	lcd_home();

	//Enable interupts
	sei();
	//Enable external interupt 1
	GICR = (1<<INT1);
	//Setup INT1 to trigger on a logical change
	MCUCR = (0<<ISC11)|(1<<ISC10);
	//Enable the overflow interupt
	TIMSK = (1<<TOIE0)|(1<<TOIE2);
	//set the clock to 1024
	TCCR0 = (1<<CS02)|(1<<CS00);
	// setup the clock to trigger every 5 ms
	TCNT0 = 175;

	//set the clock to 1024
	TCCR2 = (1<<CS22)|(1<<CS21)|(1<<CS20);
	//set TCNT2 to fire after 2.5 ms
	TCNT2 = 216;

	USARTInit(103);
	

}

void USARTInit(uint16_t ubrr_value)
{

	//9600 data rate
	//2 stop bits
	//no parity
	//no flow control
   //Set Baud rate

   UBRRL = (unsigned char) ubrr_value;
   UBRRH = (unsigned char) (ubrr_value>>8);
	
   //Enable The receiver and transmitter

   UCSRB = (1<<RXEN)|(1<<TXEN);

   UCSRC = (1<<URSEL) /* write to UCSRC */ |(1<<USBS) /* use 2 stop bits */ |(3<<UCSZ0) /* use 8-bit data frame */;


}


/*************SENDING FUNCTIONS AND ISRS*********************/

void send_message(char input[]){
	
	//write an intial 1
	//write1();
	char dest[2];
	dest[0] = input[0];
	dest[1] = input[1];

	appendHeader(dest);

	//for each character
	for(int i = 2; i < 242; i++){
		
		//if reached enter character
		//then message done
		if(input[i] == 13){
			break;
		}
		writeChar(input[i]);

	}

	writeChar(0x04);

	//
	messageBuffered = true;
	DETECTEDCOLLISION = false;

}

void appendHeader(char dest[]){
	writeChar(0x01);

	uint8_t value = 0;

	value = value + 1 * (dest[1] - 0x30);
	value = value + 16 * (dest[0] - 0x30);
	
	writeChar(value);
	writeChar(ADDRESS);
	writeChar(0x02);
	
}

void writeChar(uint8_t out){
	
	
	//assign temp variable character to
	//current character being sent
	uint8_t character = out;

	for(int j = 7; j >=0; j--){
		
		//if bit in character is 0
		//send a 0
		if(((character)&(1<<j))==0){
			write0();
		}
		//else assume it a 1 and send a 1
		else{
			write1();
		}
	}
}


void write1(){
	
	out_buffer[out_buf_index] = '1';
	out_buf_index++;

	//PORTD = (0<<PD7);
	//_delay_ms(2.5);
	//PORTD = (1<<PD7);
}
void write0(){

	out_buffer[out_buf_index] = '0';
	out_buf_index++;

	//PORTD = (1<<PD7);
	//_delay_ms(2.5);
	//PORTD = (0<<PD7);
}


//from USART. This function will wait untill data is
//available.
char USARTReadChar()
{
   //Wait untill a data is available

   while((UCSRA & (1<<RXC)) == 0);

   //Now USART has got data from host
   //and is available is buffer

   return UDR;
}


/*************RECEIVING FUNCTIONS AND ISRS*********************/


ISR(INT1_vect){
	cli();
	if(IDLE){
		BITPARTTWO = true;
		LASTBITPART = 1;	
	}
	PORTB = 0b00000010;
	BUSY = true;
	IDLE = false;
	TCNT0 = 175;
	//set TCNT2 to go 1.25 ms later
	TCNT2 = 235;
	sei();
}


//This fuction writes the given "data" to
//the USART which then transmit it via TX line
void USARTWriteChar(char data)
{
   //Wait untill the transmitter is ready

   while(!(UCSRA & (1<<UDRE)))
   {
      //Do nothing
   }

   //Now write the data to USART buffer

   UDR=data;
}


void write_message_buffer(){
	
	//We are going to want to check header stuff before reading the message
	//char[18] header;
	//for(int x = 0; x < 18; x++){
	//	header[x] = 
	//} 

	if(message_buffer[0] == 0x01){
		//if((message_buffer[1] == ADDRESS) || message_buffer[1] == 0x00){
			uint16_t x = 4;
			while(x < our_index){
		
				if(message_buffer[x] == 0x04){
					break;
				}
				//read message buffer 8 bits at a time and print the chars
				USARTWriteChar(message_buffer[x]);
				x++;
			}
		//}
	}

	
	our_index = 0;
	
}

/*************MONITORING FUNCTIONS AND ISRS*********************/

//set intially to fire every 2.5 ms
//both sends and receives messages
ISR(TIMER2_OVF_vect){

	//if we are sending, we need to check the out buffer and transmit anything in it
	if(SENDING){
		if(messageBuffered){
			//if there is something in the buffer
			if( out_buf_index > 0 && ( currentOutPosition != ( out_buf_index ) ) ){
				//there is a 1 in the buffer
				uint16_t i = currentOutPosition;
				if(out_buffer[i] == '1'){
					//if the first part of the bit in the buffer has not been sent, send the first part
					if(!firstPartSent){
						PORTD = (0<<PD7);
						firstPartSent = true;
					
					}
					//send the second part
					else{
						PORTD = (1<<PD7);
						firstPartSent = false;
						currentOutPosition++;
					
					}
				}
				//there is a 0 in the buffer
				else{
					//if the first part of the bit in the buffer has not been sent, send the first part
					if(!firstPartSent){
						PORTD = (1<<PD7);
						firstPartSent = true;
					
					}
					//send the second part
					else{
						PORTD = (0<<PD7);
						firstPartSent = false;
						currentOutPosition++;
					
					}
				}
			}
			else{
				SENDING = false;
				messageBuffered = false;
				firstPartSent = false;
				out_buf_index = 0;
				currentOutPosition = 0;
				PORTD = (1<<PD7);
			}
		}
	}
	else if(BUSY){

		if(BITPARTTWO){
		
			int IN;
			if(((PIND)&(1<<PD6))==0){
				IN = 0;
			}
			else{
				IN = 1;
			}		


			if(LASTBITPART == 1 && IN == 0){
				
				if(bitCount == 7){
					message_buffer[our_index] = currentChar;
					our_index++;
					bitCount = 0;
					currentChar = 0x00;
				}
				else{
					bitCount++;
				}

			}
			else if(LASTBITPART == 0 && IN == 1){
		

				if(bitCount == 7){
					currentChar = currentChar | 0b00000001;
					message_buffer[our_index] = currentChar;
					our_index++;
					bitCount = 0;
					currentChar = 0x00;
				}
				else if(bitCount == 6){
					currentChar = currentChar | 0b00000010;
					bitCount++;
				}
				else if(bitCount == 5){
					currentChar = currentChar | 0b00000100;
					bitCount++;
				}
				else if(bitCount == 4){
					currentChar = currentChar | 0b00001000;
					bitCount++;
				}
				else if(bitCount == 3){
					currentChar = currentChar | 0b00010000;
					bitCount++;
				}
				else if(bitCount == 2){
					currentChar = currentChar | 0b00100000;
					bitCount++;
				}
				else if(bitCount == 1){
					currentChar = currentChar | 0b01000000;
					bitCount++;
				}
				else if(bitCount == 0){
					currentChar = currentChar | 0b10000000;
					bitCount++;
				}

		
			} else {
			
				//must be either collision or idle
			}

			BITPARTTWO = false;

		}
		else{

			//must be first bit
			if(((PIND)&(1<<PD6))==0){
				LASTBITPART = 0;
			}
			else{
				LASTBITPART = 1;
			}
			BITPARTTWO = true;

		}
	}

	
	//set TCNT2 to fire after 2.5 ms
	TCNT2 = 216;

}

// initially setup to trigger every 5 ms
ISR(TIMER0_OVF_vect){
	if(((PIND)&(1<<PD6))==0){
		
			PORTB = 0b00000100;
			COLLISION =true;
			IDLE = false;
			//PORTD = (1<<PD7);
			if(SENDING){
				DETECTEDCOLLISION = true;
			}
	}
	else{
		
		if(BUSY){
			cli();
			write_message_buffer();
			sei();
		}
		PORTB = 0b00000001;
		IDLE = true;
		BUSY = false;
		COLLISION = false;
		LASTBITPART = 1;
		our_index = 0;
		BITPARTTWO = false;
		/*
		if(!SENDING){
			PORTD = (1<<PD7);
		}
		*/
		
	}

	TCNT0 =175;	
}





